{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Copyright (c) Microsoft Corporation.\n",
    "Licensed under the MIT license.\n",
    "\n",
    "# Feast Azure Provider Tutorial: Register Features\n",
    "\n",
    "In this notebook you will connect to your feature store and register features into a central repository hosted on Azure Blob Storage. It should be noted that best practice for registering features would be through a CI/CD process e.g. GitHub Actions, or Azure DevOps.\n",
    "\n",
    "## What is Feast?\n",
    "\n",
    "Feast is an operational data system for managing and serving machine learning features to models in production. Feast is able to serve feature data to models from a low-latency online store (for real-time prediction) or from an offline store (for scale-out batch scoring or model training).\n",
    "\n",
    "![feast overview](../media/feast-overview.png)\n",
    "\n",
    "## Configure Feature Repository\n",
    "\n",
    "The cell below displays the feature_store.yaml file - a file that contains infrastructural configuration, such as where the registry file is located, and connection strings to data.\n",
    "\n",
    "__There is no need to change the details in this file. When you connect to the feature store afterwards, the credentials are resolved from the Azure ML default keyvault.__"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "!cat feature_repo/feature_store.yaml"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Connect to the feature store\n",
    "\n",
    "Below you connect to the feature store."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "from feast import FeatureStore\n",
    "from azureml.core import Workspace\n",
    "\n",
    "# access key vault to get secrets\n",
    "ws = Workspace.from_config()\n",
    "kv = ws.get_default_keyvault()\n",
    "os.environ['REGISTRY_PATH']=kv.get_secret(\"FEAST-REGISTRY-PATH\")\n",
    "os.environ['SQL_CONN']=kv.get_secret(\"FEAST-OFFLINE-STORE-CONN\")\n",
    "os.environ['REDIS_CONN']=kv.get_secret(\"FEAST-ONLINE-STORE-CONN\")\n",
    "\n",
    "# connect to feature store\n",
    "fs = FeatureStore(\"./feature_repo\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Define the data source (offline store)\n",
    "\n",
    "The data source refers to raw underlying data (a table in Azure SQL DB or Synapse SQL). Feast uses a time-series data model to represent data. This data model is used to interpret feature data in data sources in order to build training datasets or when materializing features into an online store."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from feast.infra.offline_stores.contrib.mssql_offline_store.mssqlserver_source import MsSqlServerSource\n",
    "\n",
    "orders_table = \"orders\"\n",
    "driver_hourly_table = \"driver_hourly\"\n",
    "customer_profile_table = \"customer_profile\"\n",
    "\n",
    "driver_source = MsSqlServerSource(\n",
    "    table_ref=driver_hourly_table,\n",
    "    event_timestamp_column=\"datetime\",\n",
    "    created_timestamp_column=\"created\",\n",
    ")\n",
    "\n",
    "customer_source = MsSqlServerSource(\n",
    "    table_ref=customer_profile_table,\n",
    "    event_timestamp_column=\"datetime\",\n",
    "    created_timestamp_column=\"\",\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Define Feature Views\n",
    "\n",
    "A feature view is an object that represents a logical group of time-series feature data as it is found in a data source. Feature views consist of one or more entities, features, and a data source. Feature views allow Feast to model your existing feature data in a consistent way in both an offline (training) and online (serving) environment.\n",
    "\n",
    "Feature views are used during:\n",
    "\n",
    "- The generation of training datasets by querying the data source of feature views in order to find historical feature values. A single training dataset may consist of features from multiple feature views. \n",
    "- Loading of feature values into an online store. Feature views determine the storage schema in the online store.\n",
    "- Retrieval of features from the online store. Feature views provide the schema definition to Feast in order to look up features from the online store.\n",
    "\n",
    "__NOTE: Feast does not generate feature values. It acts as the ingestion and serving system. The data sources described within feature views should reference feature values in their already computed form.__"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from feast import Feature, FeatureView, ValueType\n",
    "from datetime import timedelta\n",
    "\n",
    "driver_fv = FeatureView(\n",
    "    name=\"driver_stats\",\n",
    "    entities=[\"driver\"],\n",
    "    features=[\n",
    "        Feature(name=\"conv_rate\", dtype=ValueType.FLOAT),\n",
    "        Feature(name=\"acc_rate\", dtype=ValueType.FLOAT),\n",
    "        Feature(name=\"avg_daily_trips\", dtype=ValueType.INT32),\n",
    "    ],\n",
    "    batch_source=driver_source,\n",
    "    ttl=timedelta(hours=2),\n",
    ")\n",
    "\n",
    "customer_fv = FeatureView(\n",
    "    name=\"customer_profile\",\n",
    "    entities=[\"customer_id\"],\n",
    "    features=[\n",
    "        Feature(name=\"current_balance\", dtype=ValueType.FLOAT),\n",
    "        Feature(name=\"avg_passenger_count\", dtype=ValueType.FLOAT),\n",
    "        Feature(name=\"lifetime_trip_count\", dtype=ValueType.INT32),\n",
    "    ],\n",
    "    batch_source=customer_source,\n",
    "    ttl=timedelta(days=2),\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Define entities\n",
    "\n",
    "An entity is a collection of semantically related features. Users define entities to map to the domain of their use case. For example, a ride-hailing service could have customers and drivers as their entities, which group related features that correspond to these customers and drivers.\n",
    "\n",
    "Entities are defined as part of feature views. Entities are used to identify the primary key on which feature values should be stored and retrieved. These keys are used during the lookup of feature values from the online store and the join process in point-in-time joins. It is possible to define composite entities (more than one entity object) in a feature view.\n",
    "Entities should be reused across feature views.\n",
    "\n",
    "## Entity key\n",
    "\n",
    "A related concept is an entity key. These are one or more entity values that uniquely describe a feature view record. In the case of an entity (like a driver) that only has a single entity field, the entity is an entity key. However, it is also possible for an entity key to consist of multiple entity values. For example, a feature view with the composite entity of (customer, country) might have an entity key of (1001, 5).\n",
    "\n",
    "Entity keys act as primary keys. They are used during the lookup of features from the online store, and they are also used to match feature rows across feature views during point-in-time joins."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from feast import Entity\n",
    "driver = Entity(name=\"driver\", join_key=\"driver_id\", value_type=ValueType.INT64)\n",
    "customer = Entity(name=\"customer_id\", value_type=ValueType.INT64)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Feast `apply()`\n",
    "\n",
    "Feast `apply` will:\n",
    "\n",
    "1. Feast will scan Python files in your feature repository and find all Feast object definitions, such as feature views, entities, and data sources.\n",
    "1. Feast will validate your feature definitions\n",
    "1. Feast will sync the metadata about Feast objects to the registry. If a registry does not exist, then it will be instantiated. The standard registry is a simple protobuf binary file that is stored on Azure Blob Storage.\n",
    "1. Feast CLI will create all necessary feature store infrastructure. The exact infrastructure that is deployed or configured depends on the provider configuration that you have set in feature_store.yaml."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "fs.apply([driver, driver_fv, customer, customer_fv])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## What just happened?\n",
    "\n",
    "If you look in your feast registry storage account, you will see there is now a registry.db file that contains the metadata for your registered features. Below you can list the feature views:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import pandas as pd\n",
    "from google.protobuf.json_format import MessageToDict\n",
    "\n",
    "for x in fs.list_feature_views():\n",
    "    d=MessageToDict(x.to_proto())\n",
    "    print(\"🪟 Feature view name:\", d['spec']['name'])\n",
    "    print(\"🧑 Entities:\", d['spec']['entities'])\n",
    "    print(\"🧪 Features:\",  d['spec']['features'])\n",
    "    print(\"💾 Batch source type:\",  d['spec']['batchSource']['dataSourceClassType'])\n",
    "    print(\"\\n\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Next Steps\n",
    "In the [next part of this tutorial](./part3-train-and-deploy-with-feast.ipynb) you will:\n",
    "\n",
    "- Train a model using features stored in your feature store\n",
    "- Materialize the data from the offline store to the online store\n",
    "- Deploy the model to a real-time endpoint, that consumes feature vectors from the online store.\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3.8.12 64-bit ('feast_env')",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.12"
  },
  "orig_nbformat": 4,
  "vscode": {
   "interpreter": {
    "hash": "b81e56dd72a0de84f7bcdac7bc848ecf5d1ed9826cc75d6e0cb7b6dbe5b95a6d"
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
